# -*- coding:utf-8 -*-
import time
import traceback

# @Time    : 2023/8/31 13:54
# @Author  : zengwenjia
# @Email   : zengwenjia@lingxi.ai
# @File    : sales.py
# @Software: LLM_internal

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
import sys

sys.path.append('../../')
from bot.bot import Bot
from common.log import logger
import asyncio
from common import constants
from bot.financial_wechat.agent.user_questioning import QuestioningDisputeResolution
from bot.financial_wechat.agent.determine_solution import DetermineSolution
from bot.financial_wechat.agent.conversation_agent import Conversation
from bot.financial_wechat.agent.conversation_generate_agent import ConversationGenerate
from bot.financial_wechat.agent.choose_sales_skill import SalesSkill
from bot.financial_wechat.knowledge_base.knowledge_embedding import AZUREQuery, BGEQuery
from bot.financial_wechat.agent.user_situation import UserSituation, UserSituation_History_Dialogue
import pandas as pd
import copy
from datetime import datetime
from util_tool import utils


class Sales(Bot):
    """销售机器人"""

    # 老知识库
    knowledge_base_query = BGEQuery()  # AZUREQuery() #
    knowledge_base_query.load_knowledge_base()
    file_name_content_dict = knowledge_base_query.get_file_name_content_dict()

    situation_dict = {"未安装APP": "有无APP",
                      "有京东APP": "有无APP",
                      "有京东金融APP": "有无APP",
                      "未打开APP": "是否已打开APP",
                      "已打开APP": "是否已打开APP"}

    need_count_labels = ['不需要', '在忙', '会考虑', '稍后看',  '申请渠道',
                         '有人联系过', '调侃', '自己操作', '非本人',  '操作麻烦', '系统问题',
                         '机器人']

    def __init__(self, model_name="financial_loan_7b", response_generate_method="choice"):
        self.max_token_length = 1000
        self.role_name = "销售员"
        self.model_name = model_name
        self.response_generate_method = response_generate_method
        # self.model_name = "financial_loan_7b"
        self.sales_skill_utter_set = []
        self.background_knowledge = []
        self.knowledge_solution_set = []
        self.wavno_T_choiceno_dict = dict()
        self.user_situation = dict()
        self.user_situation_prompt = ""
        self.current_conversation_stage = ""

        self.user_question = ""
        self.user_question_prompt = ""
        self.sales_skill_name = ""
        self.sales_skill_name_prompt = ""
        self.solution = ""
        self.solution_prompt = ""
        self.conversation_prompt = ""
        self.solution_knowledge = ""
        self.skill_knowledge = ""

    def format_conversation_history(self, conversation_context, max_token_length=None):
        dialogue_history = ""
        self.last_skill_name = ""
        if (not max_token_length) or (max_token_length > self.max_token_length):
            max_token_length = self.max_token_length

        # 倒序遍历record_list
        for record in conversation_context[::-1]:
            next_dialogue_history = dialogue_history
            if record['role'] == constants.ROLE_USER:
                next_dialogue_history = "用户:" + record["content"] + "\n" + dialogue_history
            elif record['role'] == constants.ROLE_ASSISTANT:
                print("format_conversation_history-content:{}".format(record["content"]))
                print('dialogue_history:{}'.format(dialogue_history))

                next_dialogue_history = self.role_name + ":" + record["content"] + "\n" + dialogue_history
            if len(next_dialogue_history) > max_token_length:
                break
            else:
                dialogue_history = next_dialogue_history

        # 倒序遍历record_list，获得最后一次销售技巧名称
        for record in conversation_context[::-1]:
            if record['role'] == constants.ROLE_ASSISTANT:
                self.last_skill_name = record["sales_skill_name"]
                break

        return dialogue_history

    async def get_user_question(self):
        start_time = time.time()
        try:
            questioning_dispute_resolution = QuestioningDisputeResolution(self.role_name,
                                                                          self.conversation_history_str)
            self.user_question = await questioning_dispute_resolution.achat_auto_llm(type=self.model_name)
            self.user_question = self.user_question.replace('"', '').strip()

            logger.info('self.conversation_history_str:{},self.model_name:{},self.user_question:{}'.format(self.conversation_history_str,self.model_name,self.user_question))

            # 用户问题归一化
            self.user_question = self.user_question.replace("近期无需求", "不需要")
            if self.user_question == "稍后看":
                self.user_question = "在忙-稍后看"
            elif self.user_question == "稍后操作":
                self.user_question = "在忙-稍后操作"
            self.user_question_prompt = questioning_dispute_resolution.prompt

            # 拒绝次数计算
            # 计算出一次拒绝，三次拒绝
            if '不需要' in self.user_question or '在忙' in self.user_question:
                refuse_times = 0
                for dialogue_info in self.context[::-1]:
                    if dialogue_info['role'] == 'assistant':
                        user_question = dialogue_info['user_question']
                        # 出现拒绝类标签计数，出现非拒绝类标签跳出循环
                        if '不需要' in user_question or '在忙' in user_question:
                            refuse_times += 1

                self.refuse_times = refuse_times
                print(self.refuse_times)

            # 累积标签计算
            need_count = False
            need_label = ""
            for label in self.need_count_labels:
                if label in self.user_question:
                    need_count = True
                    need_label = label
            if need_count:
                accumulate_time = 0
                for dialogue_info in self.context[::-1]:
                    if dialogue_info['role'] == 'assistant':
                        user_question = dialogue_info['user_question']
                        if need_label in user_question:
                            accumulate_time += 1
                if accumulate_time == 1 and ('再次' not in self.user_question) and ('多次' not in self.user_question):
                    self.user_question = '再次表示' + self.user_question
                elif accumulate_time > 1 and ('再次' not in self.user_question) and ('多次' not in self.user_question):
                    self.user_question = '多次表示' + self.user_question

            logger.info(f"session_id:{self.session_id}, 用户问题耗时:{time.time() - start_time}")
        except Exception as e:
            # 打印错误的堆栈信息
            logger.exception(e)
            self.user_question = ""

    # 根据用户问题和用户情境构建解决方案候选集
    async def get_solution_candidate(self):
        background_knowledge = []
        has_plan = False
        if ("无问题" not in self.user_question) and ("无" != self.user_question):
            similarity_knowledge = list()
            knowledge_query = "用户问题：" + self.user_question + "\t"
            # 未检索新知识或未检索到新知识
            if not similarity_knowledge:
                print("*********************查询用户问题旧知识库")
                similarity_knowledge = await self.knowledge_base_query.search_with_score(
                    knowledge_query, self.user_situation, top_k=8)
            logger.info('{},用户问题：{},用户情境：{},候选方案：{}'.format(self.session_id, self.user_question,
                                                                        self.user_situation, similarity_knowledge))
            if similarity_knowledge:
                # 判断中字符串的长度，累积长度要是大于1024的话，跳出循环
                for index, item in enumerate(similarity_knowledge):
                    if '用户问题' in item.metadata.keys():
                        metadata = item.metadata
                        page_content = "用户问题：" + str(metadata['用户问题']).strip()

                        if "用户情境" in metadata and pd.isna(metadata["用户情境"]) == False:
                            page_content += '\t用户情境：' + str(metadata['用户情境']).strip()
                        if "话术" in metadata:
                            page_content += '\t答案：' + str(metadata['话术']).strip()
                        else:
                            has_plan = True
                            page_content += '\t答案：' + str(metadata['方案']).strip()

                        background_knowledge.append(page_content)

                # 如果是纯话术，那么就不需要进行方案的判断，可以直接进行话术选择
                if background_knowledge and (not has_plan):
                    solution_utter = []
                    for index, item in enumerate(similarity_knowledge):
                        if '话术' in item.metadata.keys():

                            try:
                                if ('不需要' in self.user_question or '在忙' in self.user_question) and \
                                        self.refuse_times >= 3:

                                    # 三次拒绝只允许挂机话术进入话术集
                                    if not item.metadata.get('动作标签', '') or (
                                            item.metadata.get('动作标签', '') and item.metadata.get('动作标签',
                                                                                                    '') != '挂机'):
                                        # print(item.metadata)
                                        continue
                            except Exception as err:
                                traceback.print_exc()

                            metadata = {}
                            keys = ['沟通策略', '用户问题', '用户情境', '话术', '录音编号', '动作标签']
                            for key in keys:
                                if key in item.metadata:
                                    metadata[key] = item.metadata[key]
                            solution_utter.append(metadata)

                    if not solution_utter:
                        for index, item in enumerate(similarity_knowledge):
                            if '话术' in item.metadata.keys():
                                metadata = {}
                                keys = ['沟通策略', '用户问题', '用户情境', '话术', '录音编号', '动作标签']
                                for key in keys:
                                    if key in item.metadata:
                                        metadata[key] = item.metadata[key]
                                solution_utter.append(metadata)

                    self.knowledge_solution_set = solution_utter
                    self.background_knowledge = []
                else:
                    self.background_knowledge = background_knowledge

    async def get_user_situation(self, deal_conversation_history, base_info):
        start_time = time.time()
        logger.info('base_info：[{}]'.format(base_info))
        res_user_situation = {}
        # self.user_situation.update({'是否已打开APP': '未打开APP'})

        try:
            if len(self.context) > 6:
                user_situation = UserSituation(deal_conversation_history)
                res_user_situation_str = await user_situation.achat_auto_llm(type=self.model_name)
                self.user_situation_prompt = user_situation.prompt
                logger.info(f"1.2 User_Situation_Result: {res_user_situation_str}")
                if res_user_situation_str and "无" != res_user_situation_str:
                    res_user_situation_list = str(res_user_situation_str).strip().split(",")
                    for item in res_user_situation_list:
                        if item in self.situation_dict.keys():
                            res_user_situation[self.situation_dict[item]] = item

            # 合并用户情境
            self.user_situation.update(res_user_situation)
            # 删除值为空的键值对
            self.user_situation = {k: v for k, v in self.user_situation.items() if v}
        except Exception as e:
            logger.error(f"获取用户情境失败: 返回：{res_user_situation}, 错误：{e}")
        # logger.info('整理后的用户情境：[{}]'.format(res_user_situation))
        logger.info(f"session_id:{self.session_id}, 用户情境耗时:{time.time() - start_time}")

    async def determine_solution(self, session_id):
        start_time = time.time()
        try:
            res_design_solution = ""
            if not self.user_question and not self.user_situation:
                return res_design_solution

            if self.background_knowledge:
                design_solution = DetermineSolution(self.conversation_history_str, self.background_knowledge,
                                                    self.user_question, self.user_situation)
                res_design_solution = await design_solution.achat_auto_llm(type=self.model_name)
                self.solution_prompt = design_solution.prompt
                self.solution = res_design_solution

                self.current_conversation_stage = res_design_solution
                # logger.info(f"session_id:{session_id},整理后的解决方案是:{self.solution}")
                logger.info(f"session_id:{self.session_id}, 解决方案耗时:{time.time() - start_time}")
            else:
                self.solution = ""
                logger.info('无解决方案候选集，不走解决方案agent')

            solution_utter = []
            similarity_knowledge = []

            if self.solution:
                similarity_knowledge = await self.knowledge_base_query.search_with_score("话术：" + self.solution,
                                                                                         self.user_situation, top_k=8)

            logger.info(
                '{},用户问题：{},解决方案：{},话术集：{}'.format(self.session_id, self.user_question, self.solution,
                                                              similarity_knowledge))
            if similarity_knowledge:
                for index, item in enumerate(similarity_knowledge):
                    if '话术' in item.metadata.keys():
                        metadata = {}
                        keys = ['沟通策略', '用户情境', '话术', '用户问题', '录音编号', '动作标签']
                        for key in keys:
                            if key in item.metadata:
                                metadata[key] = item.metadata[key]
                        solution_utter.append(metadata)
            self.knowledge_solution_set = solution_utter

        except Exception as e:
            # 打印错误的堆栈信息
            logger.exception(e)
            self.solution = ""

    async def get_sales_skill(self, user_question, user_situation):
        start_time = time.time()
        if len(self.context) <= 3:
            sales_skill_name = "加微意向确认"
        else:
            skill_choose = SalesSkill(self.role_name, self.conversation_history_str, user_question, user_situation)
            sales_skill_name = await skill_choose.achat_auto_llm(type=self.model_name)
            sales_skill_name = sales_skill_name.strip()
            self.sales_skill_name_prompt = skill_choose.prompt

        # logger.info(f"session_id:{self.session_id},整理后的销售策略是:{sales_skill_name}")
        logger.info(f"session_id:{self.session_id}, 销售策略耗时:{time.time() - start_time}")

        self.sales_skill_name = sales_skill_name

        return sales_skill_name

    async def get_skill_utter_set(self):
        sales_skill_utter_set = []
        sales_skill_utter_set_backup = []
        situation_filter = False
        # 三次拒绝取消销售技巧话术集
        # 销售技巧名称为空取消销售技巧话术集
        if self.sales_skill_name != '无' and '无销售策略' not in self.sales_skill_name and \
                not (('不需要' in self.user_question or '在忙' in self.user_question) and \
                     self.refuse_times >= 3):
            queryStr = "沟通策略：" + self.sales_skill_name


            similarity_knowledge = await self.knowledge_base_query.search_with_score(queryStr,
                                                                                     self.user_situation,
                                                                                     top_k=8)
            logger.info('{},销售策略：{},销售策略话术：{}'.format(self.session_id, queryStr, similarity_knowledge))
            if similarity_knowledge:
                # 判断中字符串的长度，累积长度要是大于1024的话，跳出循环
                for index, item in enumerate(similarity_knowledge):
                    if '沟通策略' in item.metadata.keys() and '话术' in item.metadata.keys():
                        metadata = {}
                        keys = ['沟通策略', '用户情境', '话术', '录音编号', '动作标签']
                        for key in keys:
                            if key in item.metadata:
                                metadata[key] = item.metadata[key]
                        sales_skill_utter_set.append(metadata)
                # 如果被权益全过滤，仍保留
                if situation_filter and not sales_skill_utter_set:
                    sales_skill_utter_set = sales_skill_utter_set_backup

                # 第一次获取销售技巧话术集后，与已有对话记录进行过滤，之后的结果为空时，自动切换销售技巧为加微
                sales_skill_utter_set = self.delete_duplicate_user_utter(sales_skill_utter_set, 'B')

            if not sales_skill_utter_set:
                # hardcode 如果没有销售技巧话术，就用引导加微信的话术
                self.sales_skill_name = "加微意向确认"
                queryStr = "沟通策略：" + self.sales_skill_name
                similarity_knowledge = await self.knowledge_base_query.search_with_score(queryStr, self.user_situation,
                                                                                         top_k=8)

                if similarity_knowledge:
                    # 判断中字符串的长度，累积长度要是大于1024的话，跳出循环
                    for index, item in enumerate(similarity_knowledge):
                        if '沟通策略' in item.metadata.keys() and '话术' in item.metadata.keys():
                            metadata = {}
                            keys = ['沟通策略', '用户情境', '话术', '录音编号', '动作标签']
                            for key in keys:
                                if key in item.metadata:
                                    metadata[key] = item.metadata[key]
                            sales_skill_utter_set.append(metadata)
                sales_skill_utter_set = self.delete_duplicate_user_utter(sales_skill_utter_set, 'B')

        self.sales_skill_utter_set = sales_skill_utter_set

    def post_process_code(self, code):
        sep = "\n```"
        if sep in code:
            blocks = code.split(sep)
            if len(blocks) % 2 == 1:
                for i in range(1, len(blocks), 2):
                    blocks[i] = blocks[i].replace("\\_", "_")
            code = sep.join(blocks)
        return code

    # 根据历史对话记录删除重复的用户备选话术
    def delete_duplicate_user_utter(self, user_utter_set, pre=''):
        if not user_utter_set:
            return []

        assistant_content_set = set()
        for assistant_content in self.context:
            if assistant_content['role'] == constants.ROLE_ASSISTANT:
                assistant_content = assistant_content['content']
                if assistant_content:
                    assistant_content_set.add(assistant_content)
        user_utter_set_new = []
        for user_utter in user_utter_set:
            if len(user_utter['话术']) < 10 and (user_utter['话术'] not in assistant_content_set):
                user_utter_set_new.append(user_utter)
            elif len(user_utter['话术']) >= 10:
                flag = True
                for assistant_content in assistant_content_set:
                    if user_utter['话术'] in assistant_content:
                        flag = False
                        break
                if flag:
                    user_utter_set_new.append(user_utter)

        # 对知识库进行重新编号
        if pre:
            for i, user_utter in enumerate(user_utter_set_new):
                self.wavno_T_choiceno_dict['{}{}'.format(pre, i)] = user_utter['录音编号']
                user_utter['录音编号'] = '{}{}'.format(pre, i)

        return user_utter_set_new

    async def search_knowladge(self, query, user_situation):

        similarity_knowledge = await self.knowledge_base_query.search_with_score(
            query, user_situation, top_k=8)

        knowledge_json = {
            'data': list()
        }
        for index, item in enumerate(similarity_knowledge):
            knowledge_json['data'].append({
                'page_content': item.page_content,
                'meta_data': item.metadata
            })
        return knowledge_json

    async def generate_reply(self, retry=0):
        try:

            self.knowledge_solution_set = self.delete_duplicate_user_utter(self.knowledge_solution_set, 'A')
            # self.sales_skill_utter_set = self.delete_duplicate_user_utter(self.sales_skill_utter_set, 'B')
            # todo hardcode
            if ("什么平台" in self.user_question) and ("表明来意" == self.sales_skill_name) and (len(self.context) < 5):
                self.knowledge_solution_set = []

            if self.response_generate_method == 'choice':
                conversation = Conversation(self.role_name,
                                            self.conversation_history_str,
                                            self.user_question,
                                            self.solution if self.knowledge_solution_set else '',
                                            self.knowledge_solution_set,
                                            self.sales_skill_name,
                                            self.sales_skill_utter_set,
                                            self.user_situation,
                                            )
            else:
                conversation = ConversationGenerate(self.role_name,
                                                    self.conversation_history_str,
                                                    self.user_question,
                                                    self.solution if self.knowledge_solution_set else '',
                                                    self.knowledge_solution_set,
                                                    self.sales_skill_name,
                                                    self.sales_skill_utter_set,
                                                    self.user_situation,
                                                    )
                start_time = time.time()
                message = await conversation.achat_auto_llm(type=self.model_name)
                message = message.replace('LS01-1C', 'LS001-1C').strip()
                message = message.replace('LQ05-1C', 'LQ005-1C').strip()
                message = message.replace('SD010-1C', 'SD01-11C').strip()
                logger.info(f"session_id:{self.session_id}, 话术生成耗时:{time.time() - start_time}")
                return {
                    'reply_text': message,
                    'voice_record': '',
                    'label': '',
                }

            solution_voice_record_text_dict = {}
            sales_voice_record_text_dict = {}
            reply_result = {}
            start_time = time.time()
            message = await conversation.achat_auto_llm(type=self.model_name)
            message = message.replace('LS01-1C', 'LS001-1C').strip()
            message = message.replace('LQ05-1C', 'LQ005-1C').strip()
            message = message.replace('SD010-1C', 'SD01-11C').strip()
            message = message.replace('LQ01-1C', 'LQ001-1C').strip()
            logger.info(f"session_id:{self.session_id}, 话术生成耗时:{time.time() - start_time}")
            self.conversation_prompt = conversation.prompt
            # prompt日志呈现上使用原始录音编号
            # 实际使用的是选项编号
            for choice_no in self.wavno_T_choiceno_dict:
                self.conversation_prompt = self.conversation_prompt.replace(
                    choice_no, self.wavno_T_choiceno_dict[choice_no])

            if self.knowledge_solution_set:
                for item in self.knowledge_solution_set:
                    if "录音编号" in item:
                        voice_record = item["录音编号"]
                        voice_record = voice_record.strip().replace("'", "").replace("\"", "")
                        # solution_voice_record_text_dict[voice_record] = item["话术"]
                        solution_voice_record_text_dict[voice_record] = {
                            '话术': item["话术"],
                            '动作标签': item.get('动作标签', '')
                        }
            if self.sales_skill_utter_set:
                for item in self.sales_skill_utter_set:
                    if "录音编号" in item:
                        voice_record = item["录音编号"]
                        voice_record = voice_record.strip().replace("'", "").replace("\"", "")
                        # sales_voice_record_text_dict[voice_record] = item["话术"]
                        sales_voice_record_text_dict[voice_record] = {
                            '话术': item["话术"],
                            '动作标签': item.get('动作标签', '')
                        }
            logger.info(f"session_id:{self.session_id},整理后的生成回复是:{message}")
            voice_records = message.split(",")
            text_list = []
            record_list = []
            record_dict = {}
            label_result = ''

            for voice_record in voice_records:

                voice_record = voice_record.strip().replace("'", "").replace("\"", "")
                if voice_record in solution_voice_record_text_dict and "solution" not in record_dict:
                    record_dict["solution"] = {"voice_record": voice_record}
                    record_text = solution_voice_record_text_dict[voice_record]['话术']
                    label = solution_voice_record_text_dict[voice_record]['动作标签']
                    record_dict["solution"]["record_text"] = record_text
                    record_dict["solution"]["label"] = label

                    if str(record_text).endswith("?") or str(record_text).endswith("？"):
                        record_dict["solution"]["is_question"] = True
                    else:
                        record_dict["solution"]["is_question"] = False

                    if '再见' in str(record_text):
                        record_dict["solution"]['is_end'] = True
                    else:
                        record_dict["solution"]['is_end'] = False

                elif voice_record in sales_voice_record_text_dict and "sales" not in record_dict:
                    record_dict["sales"] = {"voice_record": voice_record}
                    record_text = sales_voice_record_text_dict[voice_record]['话术']
                    label = sales_voice_record_text_dict[voice_record]['动作标签']
                    record_dict["sales"]["record_text"] = record_text
                    record_dict["sales"]["label"] = label
                    if str(record_text).endswith("?") or str(record_text).endswith("？"):
                        record_dict["sales"]["is_question"] = True
                    else:
                        record_dict["sales"]["is_question"] = False

                    if '再见' in str(record_text):
                        record_dict["sales"]['is_end'] = True
                    else:
                        record_dict["sales"]['is_end'] = False
            if not record_dict and retry < 2:
                wav_no = ''
                for wav_no in sales_voice_record_text_dict:
                    break
                if wav_no:
                    first_skill_record = sales_voice_record_text_dict[wav_no]
                    record_dict = {
                        "sales": {
                            'voice_record': wav_no,
                            'record_text': first_skill_record['话术'],
                            'label': first_skill_record['动作标签']
                        }
                    }
                # return await self.generate_reply(retry=retry + 1)

            # 先返回解决方案，再返回销售技巧，如果解决方案是问题，那么就不返回销售技巧
            if "solution" in record_dict:
                text_list.append(record_dict["solution"]["record_text"])
                record_list.append(self.wavno_T_choiceno_dict[record_dict["solution"]["voice_record"]])
                # record_list.append(record_dict["solution"]["voice_record"])
                label_result = record_dict["solution"]["label"]

            if "sales" in record_dict:
                if "solution" in record_dict and record_dict["solution"]["is_question"]:
                    pass
                elif "solution" in record_dict and record_dict["solution"]["is_end"]:
                    pass
                else:
                    text_list.append(record_dict["sales"]["record_text"])
                    record_list.append(self.wavno_T_choiceno_dict[record_dict["sales"]["voice_record"]])
                    # record_list.append(record_dict["sales"]["voice_record"])
                    if not label_result:
                        label_result = record_dict["sales"]["label"]

            text = " ".join(text_list)
            voice_record = ",".join(record_list)
            reply_result['reply_text'] = text
            reply_result["voice_record"] = voice_record
            reply_result['label'] = label_result
            return reply_result
        except Exception as e:
            # 打印错误的堆栈信息
            logger.exception(e)
            return "出错了，我不知道怎么回答你的问题，你可以换个问法试试"

    async def async_reply(self, context, session_id, base_info):
        timestamp = time.time()
        date_time = int(timestamp * 1000)
        conversation = self.format_conversation_history(context)
        logger.info("conversation is " + str(conversation) + " session_id is " + str(session_id))
        self.session_id = session_id
        self.context = context
        self.conversation_history_str = conversation

        self.refuse_times = 0  # 当通对话拒绝次数
        self.case_info = ''  # 历史通话的重要信息，以一句话来表示

        base_info = copy.deepcopy(base_info)
        if 'case_info' in base_info:
            self.case_info = base_info['case_info']
            base_info.pop('case_info')

        self.base_info = base_info
        self.user_situation = base_info
        reply_result = {}
        # 使用deepcopy来复制整个列表，包括列表内的字典对象
        context_copy = copy.deepcopy(self.context)
        if len(context_copy) > 1:
            merged_data = [context_copy[0]]
            for item in context_copy[1:]:
                # 确保query_time是datetime对象
                if isinstance(item['query_time'], str):
                    item['query_time'] = datetime.strptime(item['query_time'], '%Y-%m-%d %H:%M:%S')
                if isinstance(merged_data[-1]['query_time'], str):
                    merged_data[-1]['query_time'] = datetime.strptime(merged_data[-1]['query_time'],
                                                                      '%Y-%m-%d %H:%M:%S')
                # 如果当前元素的role与结果列表中最后一个元素的role相同
                if item['role'] == merged_data[-1]['role']:
                    # 如果后面的content为'@@quiet@@'
                    if item['content'] == '@@quiet@@':
                        # 计算时间间隔
                        time_diff = item['query_time'] - merged_data[-1]['query_time']
                        # 如果时间间隔在10秒内，执行拼接
                        if time_diff.total_seconds() <= 10:
                            merged_data[-1]['content'] += '，' + item['content']
                        else:
                            # 如果时间间隔超过10秒，则删除前一个元素，只留下'@@quiet@@'的元素
                            merged_data.pop()  # 删除最后一个元素
                            merged_data.append(item)  # 添加'@@quiet@@'的元素
                    else:
                        # 如果content不是'@@quiet@@'，正常拼接
                        merged_data[-1]['content'] += '，' + item['content']
                else:
                    # 如果role不同，则将当前元素添加到结果列表
                    merged_data.append(item)
            user_replies = [reply.get('content') for reply in merged_data if reply.get('role') == 'user']
            for item in context_copy:
                # 确保query_time是datetime对象
                if isinstance(item['query_time'], datetime):
                    # 将datetime对象转换回字符串格式
                    item['query_time'] = item['query_time'].strftime('%Y-%m-%d %H:%M:%S')
            if user_replies and user_replies[-1] == '@@quiet@@':
                # 连续静音次数：从列表末尾开始计算连续的'@@quiet@@'
                count_consecutive_quiet = 0
                for content in reversed(user_replies):
                    if content == '@@quiet@@':
                        count_consecutive_quiet += 1
                    else:
                        # 一旦碰到不是'@@quiet@@'，连续统计结束
                        break
                # 累积静音次数：计算整个列表中所有'@@quiet@@'的数量
                count_latest_quiet = user_replies.count('@@quiet@@')

                # 根据静音的次数返回不同的结果
                if count_consecutive_quiet >= 3 or count_latest_quiet >= 5:
                    logger.info(f"session_id:{self.session_id},命中静音规则:连续3次或累积5次静音挂机")
                    reply_result['reply_text'] = '那等您方便的时候我们再联系吧，祝您生活愉快，再见！'
                    reply_result["voice_record"] = 'S03'
                    reply_result['label'] = '挂机'
                elif count_consecutive_quiet == 2:
                    logger.info(f"session_id:{self.session_id},命中静音规则:第2次静音")
                    reply_result['reply_text'] = '不好意思哦，刚没太听清，您说什么？ '
                    reply_result["voice_record"] = 'S02'
                    reply_result['label'] = ''
                elif count_consecutive_quiet == 1:
                    logger.info(f"session_id:{self.session_id},命中静音规则:第1次静音")
                    reply_result['reply_text'] = '您好，您这边能听到吗? '
                    reply_result["voice_record"] = 'S01'
                    reply_result['label'] = ''
            else:
                '''
                1 识别用户问题，搜索用户问题-解决方案集，用户情境
                2 确定销售策略，搜索销售策略-话术集
                3 确定最终解决方案（唯一），搜索解决方案话术集
                4 话术生成，回复客户
                '''
                # 1. 用户问题&用户问题-解决方案&用户情境
                start_time = time.time()
                user_question_task = self.get_user_question()
                user_situation_str = self.get_user_situation(self.conversation_history_str, self.base_info)
                sales_skill_task = self.get_sales_skill(self.user_question, self.user_situation)

                # 并行执行
                await asyncio.gather(user_question_task, user_situation_str,sales_skill_task)
                logger.info(
                    "sessionId:{} question situation skill spend 耗时 :{}".format(session_id, time.time() - start_time))

                # 2.根据用户问题和用户情境构建解决方案候选集
                start_time = time.time()
                solution_candidate_task = self.get_solution_candidate()
                skill_utter_task = self.get_skill_utter_set()

                # 3.销售策略&销售策略-话术集
                await asyncio.gather(solution_candidate_task, skill_utter_task)
                logger.info("the knowledge search spend is :{}".format(time.time() - start_time))

                # 4. 确定唯一解决方案
                if not self.knowledge_solution_set:
                    start_time = time.time()
                    solution_task = self.determine_solution(session_id)
                    await solution_task
                    logger.info("the solution spend is :{}".format(time.time() - start_time))
                else:
                    logger.info('不走解决方案agent')

                # 5. 话术生成，回复客户
                start_time = time.time()
                reply_result = await self.generate_reply()
                logger.info("the response generate spend is :{}".format(time.time() - start_time))
        else:
            if base_info.get('项目','') == '京东新购酒水':
                reply_text = "喂，诶您好，我是京东的客服，现在我们邀请部分优质用户进入京东官方的酒水会员群，进群后会给到您大额优惠券，同时享受超低会员价格，我先加您微信，稍后拉您进群享受优惠，好吧？"
                reply_result["reply_text"] = reply_text
                reply_result['label'] = ''
                if reply_text in self.file_name_content_dict:
                    voice_record = self.file_name_content_dict[reply_text]
                    reply_result["voice_record"] = voice_record
            else:
                full_name = base_info.get('用户姓名', '')
                if not full_name:
                    full_name = '机主本人'
                reply_text = "喂您好，请问您是{}吗？".format(full_name)
                reply_result["reply_text"] = reply_text
                reply_result['label'] = ''
                if reply_text in self.file_name_content_dict:
                    voice_record = self.file_name_content_dict[reply_text]
                    reply_result["voice_record"] = voice_record

        logger.info('执行时间:{} sessionId:{},回复话术:{}'.format(date_time, self.session_id, reply_result))

        logger.info('执行时间:{} sessionId:{},对话历史记录:{}'.format(date_time, self.session_id, context))

        # 添加过程prompt，过程结果
        reply_result['user_question_prompt'] = self.user_question_prompt
        reply_result['user_question'] = self.user_question
        logger.info(
            '执行时间:{} sessionId:{},用户问题prompt:{},用户问题:{}'.format(date_time, self.session_id,
                                                                            self.user_question_prompt,
                                                                            self.user_question))

        reply_result['sales_skill_name_prompt'] = self.sales_skill_name_prompt
        reply_result['sales_skill_name'] = self.sales_skill_name
        logger.info(
            '执行时间:{} sessionId:{},销售技巧prompt:{},销售技巧:{}'.format(date_time, self.session_id,
                                                                            self.sales_skill_name_prompt,
                                                                            self.sales_skill_name))

        reply_result['user_situation_prompt'] = self.user_situation_prompt
        reply_result['user_situation'] = self.user_situation
        logger.info(
            '执行时间:{} sessionId:{},用户信息prompt:{},用户信息:{}'.format(date_time, self.session_id,
                                                                            self.user_situation_prompt,
                                                                            self.user_situation))

        reply_result['solution_prompt'] = self.solution_prompt
        reply_result['solution'] = self.solution
        logger.info(
            '执行时间:{} sessionId:{},解决方案prompt:{},解决方案:{}'.format(date_time, self.session_id,
                                                                            self.solution_prompt, self.solution))

        reply_result['conversation_prompt'] = self.conversation_prompt
        logger.info(
            '执行时间:{} sessionId:{},话术生成prompt:{}  话术结果:{}'.format(date_time, self.session_id,
                                                                             self.conversation_prompt,
                                                                             reply_result.get('voice_record', '')))

        reply_result['solution_knowledge'] = self.knowledge_solution_set
        logger.info(
            '执行时间:{} sessionId:{},解决方案话术集:{}'.format(date_time, self.session_id,
                                                                self.knowledge_solution_set))
        reply_result['skill_knowledge'] = self.sales_skill_utter_set
        logger.info(
            '执行时间:{} sessionId:{},销售技巧话术集:{}'.format(date_time, self.session_id, self.sales_skill_utter_set))

        logger.info(
            '执行时间:{} sessionId:{},返回话术:{}'.format(date_time, self.session_id, reply_result['reply_text']))
        time_diff = (time.time() - timestamp) * 1000
        logger.info('[async_reply] sessionId:{} 执行时间:{} 整体耗时:{}'.format(session_id, date_time, time_diff))
        if time_diff > 1000:
            logger.info('[async_reply] sessionId:{} 执行时间:{} 内部执行超时:{}'.format(session_id, date_time, time_diff))
        return reply_result


class Sales_Decision(Bot):
    situation_dict = {"表述不需要": "历史对话",
                      "表述在忙": "历史对话"}

    def __init__(self, model_name="financial_loan_7b", ):
        self.max_token_length = 1000
        self.role_name = "销售员"
        self.model_name = model_name

    def format_conversation_history(self, conversation_context, max_token_length=None):
        dialogue_history = ""
        if (not max_token_length) or (max_token_length > self.max_token_length):
            max_token_length = self.max_token_length
        # 倒序遍历record_list
        for record in conversation_context[::-1]:
            next_dialogue_history = dialogue_history
            if record['role'] == constants.ROLE_USER:
                next_dialogue_history = "用户:" + record["content"] + "\n" + dialogue_history
            elif record['role'] == constants.ROLE_ASSISTANT:
                next_dialogue_history = self.role_name + ":" + record["content"] + "\n" + dialogue_history
            if len(next_dialogue_history) > max_token_length:
                break
            else:
                dialogue_history = next_dialogue_history
        return dialogue_history

    async def get_user_situation(self, deal_conversation_history, base_info):
        start_time = time.time()
        logger.info('base_info：[{}]'.format(base_info))
        res_user_situation = {}
        self.user_situation.update({'是否已打开APP': '未打开APP'})

        try:
            user_situation = UserSituation_History_Dialogue(deal_conversation_history)
            res_user_situation_str = await user_situation.achat_auto_llm(type=self.model_name)
            self.user_situation_prompt = user_situation.prompt
            logger.info(f"1.2 User_Situation_Result: {res_user_situation_str}")
            if res_user_situation_str and "无" != res_user_situation_str:
                res_user_situation_list = str(res_user_situation_str).strip().split(",")
                for item in res_user_situation_list:
                    if item in self.situation_dict.keys():
                        res_user_situation[self.situation_dict[item]] = item

            # 合并用户情境
            self.user_situation.update(res_user_situation)
            # 删除值为空的键值对
            self.user_situation = {k: v for k, v in self.user_situation.items() if v}
        except Exception as e:
            logger.error(f"获取用户情境失败: 返回：{res_user_situation}, 错误：{e}")
        # logger.info('整理后的用户情境：[{}]'.format(res_user_situation))
        logger.info(f"session_id:{self.session_id}, 用户情境耗时:{time.time() - start_time}")

    async def async_reply(self, context, session_id, base_info):

        # conversation = self.format_conversation_history(context)
        conversation = context
        logger.info("conversation is " + str(conversation) + " session_id is " + str(session_id))
        self.session_id = session_id
        self.context = context
        self.conversation_history_str = conversation

        self.base_info = base_info
        self.user_situation = base_info

        user_situation_str = await self.get_user_situation(self.conversation_history_str, self.base_info)

        print(self.user_situation)
        return self.user_situation


import asyncio
import random


class SaleGPT:
    def __init__(self):
        self.base_info_dict = {}
        self.cache_dict = {}

    def reply(self, query, session_id=None):
        name_list = ['王女士', '李先生', '张女士', '吴先生', '赵先生']
        age_list = [35, 23, 45, 21, 38]
        rights_list = ['已获得优惠券', '已获得降息', '无权益', '已获得提额', '已降息已提额', '已提额且有券']
        if session_id not in self.base_info_dict:
            name = random.choice(name_list)
            age = random.choice(age_list)
            right = random.choice(rights_list)
            self.base_info_dict[session_id] = {'用户姓名': name, '用户年龄': str(age), '初始用户权益': right}
        base_info = self.base_info_dict[session_id]
        if session_id not in self.cache_dict:
            self.cache_dict[session_id] = []
        conversation_dict = {}
        conversation_dict["role"] = "user"
        conversation_dict["content"] = query
        query_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        conversation_dict["query_time"] = query_time
        self.cache_dict[session_id].append(conversation_dict)
        conversation_history = self.cache_dict.get(session_id)
        sales = Sales()

        reply_result = asyncio.run(
            sales.async_reply(conversation_history, session_id, base_info))
        result = reply_result['reply_text']
        conversation_dict = {}
        conversation_dict["role"] = "assistant"
        conversation_dict["content"] = result
        query_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        conversation_dict["query_time"] = query_time
        self.cache_dict[session_id].append(conversation_dict)
        if '再见' in result:
            result = result + '@hang_up@'
        return result


def save_dialogue(dialogueList, filePath='dialogue.json', encoding='utf-8'):
    for rowIndex, dialogue in enumerate(dialogueList):
        with open(filePath, "a", newline="", encoding='utf-8') as f:
            f.write(dialogue.get('role') + ':' + dialogue.get('content') + '\n')
            if rowIndex == len(dialogueList) - 1:
                f.write('\n')


if __name__ == '__main__':
    local_test_batch = False
    history_dialogue = False
    import uuid
    from random import choice

    total = 5
    current_total = 0

    # base_info = ['额度:9600.0000/2023-09-14']#[] # 提额
    name_list = ['王女士', '李先生', '张女士', '吴先生']
    # age_list = [35, 23, 45, 21]
    # rights_list = ['已获得优惠券', '已获得降息', '无权益', '已获得提额', '已获得优惠券,已获得降息',
    #                '已获得提额,已获得降息', '已获得优惠券,已获得提额', '已获得优惠券,已获得降息,已获得提额']
    # rightCutRate = ['', '利息降幅大', '利息降到最低']
    # cut_rate_day = ['降息0天到期', '降息1天到期', '降息2天到期', '降息3天到期', '降息4天到期', '降息5天到期',
    #                 '降息6天到期', '降息7天到期', '降息8天后到期']
    call_count_list = ['1-call', '']
    # user_stage_list = ['', '成长期', '成熟期']
    # available_limit_list = ['额度够', '额度不够']
    # increase_opportunities_list = ['主动提额资格', '']
    # certificate_name_list = ['', '0元88折息费活动券']
    low_interest_strength_list = ['']

    # total_amount_data_list = ['总额度超10万', '总额度超10万']
    # rest_amount_data_list = ['可用额度为负', '可用额度0-1000']
    now_cut_rate_list = [] # '优化额度利率'
    # not_cut_rate_list = ['降息前费率65', '降息前费率25']
    # not_right_list = ['', '没有提额', '没有降息']
    product_list = ['京东新购酒水'] # ,'360加微'  京东金融
    from random import choice

    name = choice(name_list)
    #
    # age = choice(age_list)
    #
    # right = choice(rights_list)
    call_count = choice(call_count_list)
    product_name = choice(product_list)
    now_cut_rate = ''
    if len(now_cut_rate_list)>0:
        now_cut_rate = choice(now_cut_rate_list)
    # user_stage = choice(user_stage_list)
    # available_limit = choice(available_limit_list)
    # increase_opportunities = choice(increase_opportunities_list)
    # certificate_name = choice(certificate_name_list)
    # cutRate = ''
    # cutReteDay = ''
    # low_interest_strength = ''
    # withdrawal_strength = ''
    # total_amount_data = ''
    # rest_amount_data = ''
    # now_cut_rate = ''
    # not_right = ''
    # if '已获得降息' in right:
    #     cutRate = choice(rightCutRate)
    #     cutReteDay = choice(cut_rate_day)
    #     low_interest_strength = choice(low_interest_strength_list)
    #     rest_amount_data = choice(now_cut_rate_list)
    # else:
    #     rest_amount_data = choice(not_cut_rate_list)
    # if '已获得提额' == right:
    #     not_right = '没有降息'
    # elif '已获得降息' == right:
    #     not_right = '没有提额'
    # total_amount_data = choice(total_amount_data_list)
    # rest_amount_data = choice(rest_amount_data_list)
    # if '已获得提额' in right:
    #     withdrawal_strength = choice(withdrawal_strength_list)

    base_info = {'用户姓名': name,
                  'case_info': '之前在忙',
                 'call次': call_count,
                 '项目': product_name,
                 '活动权益':now_cut_rate}

    conversation_template_list = [
        ['谁呀？','好的。','嗯，打开了','啊。@@quiet@@]'],
    ['哦对。',
'嗯，可以',
'嗯，有'],
    []]

    if local_test_batch:
        from user import User,User_1
        import user

        conversation_template = choice(conversation_template_list)

        while True:
            if current_total >= total:
                logger.info('模拟对话完成{}通'.format(current_total))
                break
            conversation_history = []

            sessionId = str(uuid.uuid4())
            while True:
                sales = Sales('gpt')
                reply_result = asyncio.run(sales.async_reply(conversation_history, sessionId, base_info))
                # conversation_dict = {}
                # conversation_dict["role"] = "assistant"
                # conversation_dict["content"] = result
                # conversation_history.append(conversation_dict)
                # print(result)
                result = reply_result['reply_text']
                conversation_dict = {}
                conversation_dict["role"] = "assistant"
                conversation_dict["content"] = result
                query_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                conversation_dict["query_time"] = query_time
                conversation_dict["user_question"] = reply_result['user_question']
                conversation_dict["sales_skill_name"] = reply_result['sales_skill_name']
                conversation_history.append(conversation_dict)
                print(result)
                # 销售员挂机
                if '再见' in result:
                    current_total += 1
                    logger.info('------\n{}'.format(conversation_history))
                    save_dialogue(conversation_history)
                    time.sleep(2)
                    break

                user = User_1(conversation_history)
                user_input = asyncio.run(user.achat_auto_llm(type='gpt'))
                print(user_input)

                # conversation_dict = {}
                # conversation_dict["role"] = "user"
                # conversation_dict["content"] = str(user_input)
                # conversation_history.append(conversation_dict)
                conversation_dict = {}
                conversation_dict["role"] = "user"
                conversation_dict["content"] = user_input
                query_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                conversation_dict["query_time"] = query_time
                conversation_history.append(conversation_dict)
                # await user_input
                # 用户挂机
                if '<end>' in user_input:
                    current_total += 1
                    logger.info('------\n{}'.format(conversation_history))
                    save_dialogue(conversation_history)
                    time.sleep(2)
                    break
    elif history_dialogue:

        conversation_history = '''
销售员:您好，请问您是周先生吗？
用户:呃，你有事你说
销售员:哎您好您好，我是京东金融客户经理工号95266，给您致电是有一个好消息哈，这不是年底了嘛，咱们金条给客户送福利，给您争取到了一个额度利息的调整机会，具体以系统审核为准哈，您看您对现在的额度利息还满意吗？
用户:先不用了啊，谢谢啊。
销售员:啊那我简单跟您说一下哈就咱们金条是可以直接提现到您的银行卡里像平时旅游啊装修啊都可以使用的，您多多提现到银行卡里使用，还有助于创造额度和利率调整的机会，那您这边打开京东app然后我带您看一下好吧？
用户:先不用了，啊嗯。
销售员:理解您，其实还是建议您这次使用下账户的，因为金条会不定期更新额度和利率，用的多调整的机会就多，不用或者用的少，额度利息就不容易保留，后续您有大额用款需求，比如说有急用现金的时候万一额度不够了，也比较着急，您这次提现到银行卡使用一下，让系统能检测到咱们有持续的需求，那您现在就使用一下好吧？
'''
        sessionId = str(uuid.uuid4())
        sales = Sales_Decision('gpt')
        reply_result = asyncio.run(sales.async_reply(conversation_history, sessionId, base_info))
        print(reply_result)

    else:
        conversation_history = []
        sessionId = str(uuid.uuid4())
        while True:
            sales = Sales('self') #gpt
            reply_result = asyncio.run(sales.async_reply(conversation_history, sessionId, base_info))
            result = reply_result['reply_text']
            conversation_dict = {}
            conversation_dict["role"] = "assistant"
            conversation_dict["content"] = result
            query_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            conversation_dict["query_time"] = query_time
            conversation_dict["user_question"] = reply_result['user_question']
            conversation_dict["sales_skill_name"] = reply_result['sales_skill_name']
            conversation_history.append(conversation_dict)
            print(result)

            if '再见' in result:
                break
            user_input = input("请输入用户的对话：")
            conversation_dict = {}
            conversation_dict["role"] = "user"
            conversation_dict["content"] = user_input
            query_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            conversation_dict["query_time"] = query_time
            conversation_history.append(conversation_dict)
